package ar.edu.utn.frba.dds.tp.code.vuelos;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.math.BigDecimal;

import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.joda.time.Minutes;
import org.uqbar.commons.model.Entity;

import uqbar.arena.persistence.annotations.PersistentClass;
import uqbar.arena.persistence.annotations.PersistentField;
import uqbar.arena.persistence.annotations.Relation;

import ar.edu.utn.frba.dds.tp.code.Asientos.AsientoDeVuelo;
import ar.edu.utn.frba.dds.tp.code.Asientos.EstadoAsiento;

import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

@PersistentClass
public class Vuelo extends Entity {

	private static final long serialVersionUID = 1L;
	
	private List<AsientoDeVuelo> asientos = new ArrayList<AsientoDeVuelo>();
	private Aerolinea aerolinea;
	private String codigoDeVuelo;
	private String origenVuelo;
	private String destinoVuelo;
	private Date fechaSalida;
	private Date fechaLlegada;
	private LocalTime horaSalida;
	private LocalTime horaLlegada;

	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++
	// ++ accesors ++++++++++++++++++++++++++++++++++++++++++
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++

	@PersistentField
	public String getCodigoDeVuelo() {
		return codigoDeVuelo;
	}
	
	public void setCodigoDeVuelo(String codigoDeVuelo) {
		this.codigoDeVuelo = codigoDeVuelo;
	}

	@PersistentField
	public String getOrigenVuelo() {
		return origenVuelo;
	}
	
	public void setOrigenVuelo(String origenVuelo) {
		this.origenVuelo = origenVuelo;
	}
	
	@PersistentField
	public String getDestinoVuelo() {
		return destinoVuelo;
	}
	
	public void setDestinoVuelo(String destinoVuelo) {
		this.destinoVuelo = destinoVuelo;
	}

	@PersistentField
	public Date getFechaSalida() {
		return fechaSalida;
	}
	
	public void setFechaSalida(Date fechaSalida) {
		this.fechaSalida = fechaSalida;
	}

	@PersistentField
	public Date getFechaLlegada() {
		return fechaLlegada;
	}
	
	public void setFechaLlegada(Date fechaLlegada) {
		this.fechaLlegada = fechaLlegada;
	}
	
	public LocalTime getHoraSalida() {
		return horaSalida;
	}

	public void setHoraSalida(LocalTime horaSalida) {
		this.horaSalida = horaSalida;
	}

	public LocalTime getHoraLlegada() {
		return horaLlegada;
	}

	public void setHoraLlegada(LocalTime horaLlegada) {
		this.horaLlegada = horaLlegada;
	}

	public Aerolinea getAerolinea() {
		return aerolinea;
	}
	
	public Vuelo setAerolinea(Aerolinea aero) {
		this.aerolinea = aero;
		return this;
	}

	@Relation
	public List<AsientoDeVuelo> getAsientos() {
		return this.asientos;
	}
	
	public void setAsientos(List<AsientoDeVuelo> asientos) {
		this.asientos = asientos;
	}
	
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	// +++ Constructor ++++++++++++++++++++++++++++++++++++++++++++
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

	public Vuelo() {
	}

	public Vuelo(String codigoDeVuelo, String origenVuelo, String destinoVuelo,
			Date fechaSalida, Date fechaLlegada, LocalTime horaSalida,
			LocalTime horaLlegada) {
		this.codigoDeVuelo = codigoDeVuelo;
		this.setOrigenVuelo(origenVuelo);
		this.setDestinoVuelo(destinoVuelo);
		this.setFechaSalida(fechaSalida);
		this.setFechaLlegada(fechaLlegada);
		this.setHoraSalida(horaSalida);
		this.setHoraLlegada(horaLlegada);

	}

	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++
	// ++ métodos de negocio ++++++++++++++++++++++++++++++++
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++

	public void agregarAsiento(AsientoDeVuelo asiento) {
		asientos.add(asiento);
	}
	public BigDecimal getPrecioAsiento(AsientoDeVuelo asiento) {
		return asiento.getPrecio().add(
				asiento.getPrecio().multiply(aerolinea.getImpuesto()));
	}

	public Vuelo asientosNoSuperOferta() {
		List<AsientoDeVuelo> asientos = Lists.newArrayList(Collections2.filter(
				this.getAsientos(), this.asientoNoEsSuperOferta()));
		this.setAsientos(asientos);
		return this;
	}

	private Predicate<AsientoDeVuelo> asientoNoEsSuperOferta() {
		Predicate<AsientoDeVuelo> predicadoSuperOferta = new Predicate<AsientoDeVuelo>() {
			public boolean apply(AsientoDeVuelo asiento) {
				return (!asiento.soySuperOferta());
			}
		};
		return predicadoSuperOferta;
	}

	public Vuelo clone() {
		Vuelo vuelo = new Vuelo(codigoDeVuelo, origenVuelo, destinoVuelo,
				fechaSalida, fechaLlegada, horaSalida, horaLlegada);
		vuelo.setAerolinea(this.getAerolinea());
		vuelo.setAsientos(this.getAsientos());
		return vuelo;
	}

	public boolean tieneAsientosLibres() {
		return Iterables.any(this.getAsientos(),
				new Predicate<AsientoDeVuelo>() {
					public boolean apply(AsientoDeVuelo asiento) {
						return asiento.getEstado().equals(
								EstadoAsiento.DISPONIBLE);
					}
				});
	}

	public Minutes tiempoDeVuelo() {

		LocalDate fechaLlegadaLD = new LocalDate(this.fechaLlegada);
		DateTime fechaYHoraLlegada = new DateTime(fechaLlegadaLD.getYear(),
				fechaLlegadaLD.getMonthOfYear(),
				fechaLlegadaLD.getDayOfMonth(),
				this.horaLlegada.getHourOfDay(),
				this.horaLlegada.getMinuteOfHour());

		LocalDate fechaSalidaLD = new LocalDate(this.fechaSalida);
		DateTime fechaYHoraSalida = new DateTime(fechaSalidaLD.getYear(),
				fechaSalidaLD.getMonthOfYear(), fechaSalidaLD.getDayOfMonth(),
				this.horaSalida.getHourOfDay(),
				this.horaSalida.getMinuteOfHour());

		return Minutes.minutesBetween(fechaYHoraSalida, fechaYHoraLlegada);

	}

	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++
	// ++ redefinición equals +++++++++++++++++++++++++++++++
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Vuelo other = (Vuelo) obj;
		if (getCodigoDeVuelo() == null) {
			if (other.getCodigoDeVuelo() != null)
				return false;
		} else if ((!getCodigoDeVuelo().equals(other.getCodigoDeVuelo()))
				|| (!getOrigenVuelo().equals(other.getOrigenVuelo()))
				|| (!getDestinoVuelo().equals(other.getDestinoVuelo()))
				|| (!getFechaSalida().equals(other.getFechaSalida()))
				|| (!getFechaLlegada().equals(other.getFechaLlegada()))
				|| (!getHoraSalida().equals(other.getHoraSalida()))
				|| (!getHoraLlegada().equals(other.getHoraLlegada())))
			return false;
		return true;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((getCodigoDeVuelo() == null) ? 0 : getCodigoDeVuelo().hashCode());
		return result;
	}

	public Integer getCantidadAsientosComprados() {
		return RegistroCompraAsientos.getInstance().getValue(
				this.getAerolinea(), this);
	}

}